/**
 * 20220117：自原hardLine中分离
 * 20220722:add annotation for constructor.
 */
import {BufferGeometry,Vector3,Sphere,Matrix4,Float32BufferAttribute,Int8BufferAttribute,Uint8BufferAttribute} from "three";
import Props from "../general/props";

const ZERO_ARRAY=[new Vector3(0,0,0),new Vector3(1e-6,0,0)];
const IM=new Matrix4().identity();

/**
 * 硬角厚度线几何体
 */
export default class HardLineGeometry extends BufferGeometry{

    /**
     * @param {Array<Vector3>} vertices 顶点序列 
     * @param {*} values 可选参数对象
     *      {
     *           arrowLengthRat:1,箭头长度比值（相对虚线+空白）
     *           arrowWidthRat:1,箭头宽比（相对线宽）
     *           arrowMode:0,箭头模式[0:none,1:right,2:left,3:double]
     *           dashSize:0.1,虚线段长（应与材质保持一致）
     *           gapSize:0.1,空白长（应与材质保持一致）
     *      }
     */
    constructor(vertices,values={}){
        super();
        this._verts=!Array.isArray(vertices)?ZERO_ARRAY:(vertices.length===0?ZERO_ARRAY:vertices);
        this._init(values);
    }

    //....................................................public.......................................................

    /**
     * 长度
     */
    get length(){
        return this._distances[(this._rectifyVerts.length*2-3)*6-1];
    }

    set arrowLengthRat(ratio){
        this._props.arrowLengthRat.value=ratio;
    }
    get arrowLengthRat(){
        return this._props.arrowLengthRat.value;
    }

    set arrowWidthRat(ratio){
        this._props.arrowWidthRat.value=ratio;
    }
    get arrowWidthRat(){
        return this._props.arrowWidthRat.value;
    }

    set arrowMode(mode){
        this._props.arrowMode.value=mode;
    }
    get arrowMode(){
        return this._props.arrowMode.value;
    }

    set dashSize(mode){
        this._props.dashSize.value=mode;
    }
    get dashSize(){
        return this._props.dashSize.value;
    }

    set gapSize(mode){
        this._props.gapSize.value=mode;
    }
    get gapSize(){
        return this._props.gapSize.value;
    }

    set vertices(vs){
        this._verts=vs;
        this.updateGeometry();
    }
    get vertices(){
        return this._verts;
    }

    /**
     * 属性对象
     */
    set props(values){
        for(let key in values){
            if(this._props[''+key+'']){
                this._props[''+key+''].value=values[''+key+''];
            }else{
                console.warn(''+key+' is not a prop of HardLineGeometry!');
            }
        }
    }
    get props(){
        return this._props;
    }

    /**
     * 更新长度属性（与虚线及UV相关）
     */
    updateDistance(matrixWorld){
        let matrix=matrixWorld||IM;
        const pushData=(i1,d)=>{
            this._distances[i1]=d;
        };
        let d0=0;
        let d1=0;
        for(let i=0;i<this._rectifyVerts.length-1;i++){
            let cp=i===0?this._tV0.copy(this._rectifyVerts[i]).applyMatrix4(matrix):this._tV2.copy(this._tV1);
            let np=this._tV1.copy(this._rectifyVerts[i+1]).applyMatrix4(matrix);
            if(i===0){
                d1=cp.distanceTo(np);
                pushData(0,d0);
                pushData(1,d0);
                pushData(2,d1);
                pushData(3,d0);
                pushData(4,d1);
                pushData(5,d1);
                d0=d1;
            }else{
                let i1=6+(i-1)*12;
                pushData(i1,d0);
                pushData(i1+1,d0);
                pushData(i1+2,d0);
                pushData(i1+3,d0);
                pushData(i1+4,d0);
                pushData(i1+5,d0);
                //
                d1+=cp.distanceTo(np);
                pushData(i1+6,d0);
                pushData(i1+7,d0);
                pushData(i1+8,d1);
                pushData(i1+9,d0);
                pushData(i1+10,d1);
                pushData(i1+11,d1);
                d0=d1;
            }
        }
        this.getAttribute('lineDistance').copyArray(this._distances);
        this.getAttribute('lineDistance').needsUpdate=true;
    }

    /**
     * 更新几何体
     */
    updateGeometry() {
        let len = this._verts.length;
        if (len < 2) {
            this._verts=ZERO_ARRAY;
        }
        this._getData();
        this.getAttribute('position').copyArray(this._positions);
        this.getAttribute('nextPosition').copyArray(this._nextPositions);
        this.getAttribute('offset').copyArray(this._offsets);
        this.getAttribute('dashMark').copyArray(this._dashMarks);
        this.updateDistance();
        this.getAttribute('position').needsUpdate = true;
        this.getAttribute('nextPosition').needsUpdate = true;
        this.getAttribute('offset').needsUpdate = true;
        this.getAttribute('dashMark').needsUpdate = true;
        this.setDrawRange(0, ((this._rectifyVerts.length) * 2 - 3) * 6);
    }

    /**
     * 计算碰撞球
     */
    computeBoundingSphere(){
        if(!this.boundingSphere)this.boundingSphere=new Sphere();
        this.boundingSphere.setFromPoints(this._verts);
    }

    copy(source,recursive=false){
        super.copy(source,recursive);
        this.props=source.props.all(true);
        this.vertices=[...source.vertices];
        this.computeBoundingSphere();
        return this;
    }

    //....................................................private.....................................................

    static distanceOfPointToVector(dv,de,v,vs,ve,point){
        dv.subVectors(v,vs);
        de.subVectors(ve,vs);
        dv.projectOnVector(de).add(vs);
        point.copy(dv);
        return v.distanceToSquared(dv);
    }

    _init(values){
        this._initProps(values);
        this._initVariable();
        this._initAttribute();
        this._initPropCallback();
    }
    _initProps(values){
        this._props=new Props({
            arrowLengthRat:values.arrowLengthRat||1,
            arrowWidthRat:values.arrowWidthRat||1,
            arrowMode:values.hasOwnProperty('arrowMode')?values.arrowMode:0,
            dashSize:values.dashSize||0.1,
            gapSize:values.gapSize||0.1,
        });
    }
    _initPropCallback(){
        this._props.onMultiKeyChange(()=>{
            this.updateGeometry();
        },'arrowMode','arrowWidthRat','arrowLengthRat','dashSize','gapSize');
    }
    _initVariable(){
        this._maxSegments=this._verts.length*2;
        let s=this._maxSegments;
        let s3=(2*s-3)*6*3;
        let s1=(2*s-3)*6;
        this._positions=new Float32Array(s3);
        this._nextPositions=new Float32Array(s3);
        this._offsets=new Int8Array(s1);
        this._distances=new Float32Array(s1);
        this._dashMarks=new Uint8Array(s1);
        this._tS=new Sphere();
        this._arrowWidth=0;
        this._arrowLength=0;
        this._rectifyVerts=[];
        this._tV0=new Vector3();
        this._tV1=new Vector3();
        this._tV2=new Vector3();
        this._tV3=new Vector3();
        this._tM=new Matrix4();
    }
    _initAttribute(){
        this._getData();
        this._setAttribute();
        this.updateDistance();
        this.setDrawRange(0,(this._rectifyVerts.length*2-3)*6);
    }
    _setAttribute(){
        this.setAttribute('position',new Float32BufferAttribute(this._positions,3));
        this.setAttribute('nextPosition',new Float32BufferAttribute(this._nextPositions,3));
        this.setAttribute('offset',new Int8BufferAttribute(this._offsets,1));
        this.setAttribute('dashMark',new Uint8BufferAttribute(this._dashMarks,1));
        this.setAttribute('lineDistance',new Float32BufferAttribute(this._distances,1));
    }
    _getData(){
        this._rectifyVerts=this._rectifyPoints(this._verts);
        if(this._rectifyVerts.length<2){
            console.warn('At least 2 points needed!');
            return;
        }
        this._reRangeData();
        switch (this._props.arrowMode.value){
            case 0:
                this._pushSolidData(this._rectifyVerts);
                break;
            case 1:
                if(this._arrowWidth===0||this._arrowLength===0){
                    this._pushSolidData(this._rectifyVerts);
                }else {
                    this._pushRightArrowData(this._rectifyVerts);
                }
                break;
            case 2:
                if(this._arrowWidth===0||this._arrowLength===0){
                    this._pushSolidData(this._rectifyVerts);
                }else {
                    this._pushLeftArrowData(this._rectifyVerts);
                }
                break;
            case 3:
                if(this._arrowWidth===0||this._arrowLength===0){
                    this._pushSolidData(this._rectifyVerts);
                }else {
                    this._pushDoubleArrowData(this._rectifyVerts);
                }
                break;
        }
    }
    _rectifyPoints(ps) {
        switch (this._props.arrowMode.value) {
            case 0:
                this._arrowWidth=0;
                this._arrowLength=0;
                return ps;
            case 1:
                return this._rectifyRightArrowPoints(ps);
            case 2:
                return this._rectifyLeftArrowPoints(ps);
            case 3:
                return this._rectifyDoubleArrowPoints(ps);
        }
    }
    _rectifyDoubleArrowPoints(ps){
        let temp=[];
        this._arrowWidth =1 + 2 * this._props.arrowWidthRat.value;
        let dAg=this._props.dashSize.value + this._props.gapSize.value;
        this._arrowLength = dAg * this._props.arrowLengthRat.value;
        let indexLeft=-1;
        let indexRight=-1;
        let iStartLeft=-1;
        let iStartRight=-1;
        let tLen=0;
        for (let i = 0; i <ps.length-1; i++) {
            tLen += ps[i].distanceTo(ps[i + 1]);
            if(tLen>0)iStartLeft=i+1;
            if (tLen >= this._arrowLength) {
                indexLeft = i + 1;
                break;
            }
        }
        tLen=0;
        for (let i = ps.length - 1; i > 0; i--) {
            tLen += ps[i].distanceTo(ps[i - 1]);
            if(tLen>0)iStartRight=i-1;
            if (tLen >= this._arrowLength) {
                indexRight = i - 1;
                break;
            }
        }
        if(iStartRight===-1||iStartLeft===-1){
            temp=ps.slice(0);
            this._arrowWidth=0;
            this._arrowLength=0;
        }else{
            if(indexLeft===-1||indexRight===-1){
                this._arrowLength=tLen;
                let dirESL = this._tV0.subVectors(ps[iStartLeft], ps[0]).normalize();
                let nStartL = new Vector3().addVectors(ps[0], dirESL.multiplyScalar(this._arrowLength));
                temp.push(ps[0],nStartL,nStartL);
                let dirESR = this._tV1.subVectors(ps[iStartRight], ps[ps.length - 1]).normalize();
                let nStartR = new Vector3().addVectors(ps[ps.length - 1], dirESR.multiplyScalar(this._arrowLength));
                temp.push(nStartR,nStartR, ps[ps.length - 1]);
            }else{
                let dpLP=new Vector3();
                let dpL=HardLineGeometry.distanceOfPointToVector(this._tV0,this._tV1,ps[0],ps[indexLeft],ps[indexLeft-1],dpLP);
                let deL=Math.sqrt(this._arrowLength*this._arrowLength-dpL);
                let dirESL=this._tV0.subVectors(ps[indexLeft],ps[indexLeft-1]).normalize();
                let nStartL=dpLP.add(dirESL.multiplyScalar(deL));
                //
                let dpRP=new Vector3();
                let dpR=HardLineGeometry.distanceOfPointToVector(this._tV0,this._tV1,ps[ps.length-1],ps[indexRight],ps[indexRight+1],dpRP);
                let deR=Math.sqrt(this._arrowLength*this._arrowLength-dpR);
                let dirESR=this._tV0.subVectors(ps[indexRight],ps[indexRight+1]).normalize();
                let nStartR=dpRP.add(dirESR.multiplyScalar(deR));

                // temp.push(ps[0],nStartL,nStartL);20.2.27
                temp.push(ps[0],nStartL);
                for(let i=indexLeft;i<indexRight+1;i++){
                    temp.push(ps[i]);
                }
                // temp.push(nStartR,nStartR,ps[ps.length-1]);20.2.27
                temp.push(nStartR,ps[ps.length-1]);
            }
        }
        return temp;
    }
    _rectifyLeftArrowPoints(ps){
        let temp = [];
        this._arrowWidth = 1 + 2 * this._props.arrowWidthRat.value;
        let dAg=this._props.dashSize.value + this._props.gapSize.value;
        this._arrowLength = dAg * this._props.arrowLengthRat.value;
        let index = -1;
        let iStart=-1;
        let tLen=0;
        for (let i = 0; i <ps.length-1; i++) {
            tLen += ps[i].distanceTo(ps[i + 1]);
            if(tLen>0)iStart=i+1;
            if (tLen >= this._arrowLength) {
                index = i + 1;
                break;
            }
        }
        if(iStart===-1){
            temp=ps.slice(0);
            this._arrowWidth=0;
            this._arrowLength=0;
        }else {
            if (index === -1) {
                this._arrowLength=tLen;
                let dirES = this._tV0.subVectors(ps[iStart], ps[0]).normalize();
                let nStart = new Vector3().addVectors(ps[0], dirES.multiplyScalar(this._arrowLength));
                temp.push(ps[0],nStart,nStart);
            } else {
                let dpP=new Vector3();
                let dp=HardLineGeometry.distanceOfPointToVector(this._tV0,this._tV1,ps[0],ps[index],ps[index-1],dpP);
                let de=Math.sqrt(this._arrowLength*this._arrowLength-dp);
                let dirES=this._tV0.subVectors(ps[index],ps[index-1]).normalize();
                let nStart=dpP.add(dirES.multiplyScalar(de));
                // temp.push(ps[0],nStart,nStart);20.2.27
                temp.push(ps[0],nStart);
                for(let i=index;i<ps.length;i++){
                    temp.push(ps[i]);
                }
            }
        }
        return temp;
    }
    _rectifyRightArrowPoints(ps){
        let temp = [];
        this._arrowWidth = 1 + 2 * this._props.arrowWidthRat.value;
        let dAg=this._props.dashSize.value + this._props.gapSize.value;
        this._arrowLength = dAg * this._props.arrowLengthRat.value;
        let index = -1;
        let iStart=-1;
        let tLen=0;
        for (let i = ps.length - 1; i > 0; i--) {
            tLen += ps[i].distanceTo(ps[i-1]);
            if(tLen>0)iStart=i-1;
            if (tLen >= this._arrowLength) {
                index = i - 1;
                break;
            }
        }
        if(iStart===-1){
            temp=ps.slice(0);
            this._arrowWidth=0;
            this._arrowLength=0;
        }else {
            if (index === -1) {
                this._arrowLength=tLen;
                let dirES = this._tV0.subVectors(ps[iStart], ps[ps.length - 1]).normalize();
                let nStart = new Vector3().addVectors(ps[ps.length - 1], dirES.multiplyScalar(this._arrowLength));
                temp.push(nStart,nStart, ps[ps.length - 1]);
            } else {
                let dpP=new Vector3();
                let dp=HardLineGeometry.distanceOfPointToVector(this._tV0,this._tV1,ps[ps.length-1],ps[index],ps[index+1],dpP);
                let de=Math.sqrt(this._arrowLength*this._arrowLength-dp);
                let dirES=this._tV0.subVectors(ps[index],ps[index+1]).normalize();
                let nStart=dpP.add(dirES.multiplyScalar(de));
                for(let i=0;i<index+1;i++){
                    temp.push(ps[i]);
                }
                // temp.push(nStart,nStart,ps[ps.length-1]);20.2.27
                temp.push(nStart,ps[ps.length-1]);
            }
        }
        return temp;
    }
    _reRangeData(){
        let reRange=this._maxSegments<this._rectifyVerts.length;
        if(reRange){
            this.dispose();
            let s=this._rectifyVerts.length*2;
            this._maxSegments=s;
            let s3=(2*s-3)*6*3;
            let s1=(2*s-3)*6;
            this._positions=new Float32Array(s3);
            this._nextPositions=new Float32Array(s3);
            this._offsets=new Int8Array(s1);
            this._distances=new Float32Array(s1);
            this._dashMarks=new Uint8Array(s1);
            this._setAttribute();
        }
        return reRange;
    }
    _pushDoubleArrowData(vs){
        let n0=this._tV0;
        let n1=this._tV1;
        for(let i=0;i<vs.length-1;i++){
            let cp=vs[i];
            let np=vs[i+1];
            if(i===0){
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(0,0,cp,n0,0,0);
                this._pushData(3,1,cp,n0,0,0);
                this._pushData(6,2,np,n1,this._arrowWidth,0);
                this._pushData(9,3,cp,n0,0,0);
                this._pushData(12,4,np,n1,-this._arrowWidth,0);
                this._pushData(15,5,np,n1,this._arrowWidth,0);
            }else if(i===vs.length-2){
                let i3=18+(i-1)*36;
                let i1=6+(i-1)*12;
                n0.subVectors(cp,vs[i-1]).normalize().add(cp);
                n1.copy(np);
                this._pushData(i3,i1,cp,n0,1,1);
                this._pushData(i3+3,i1+1,cp,n0,0,1);
                this._pushData(i3+6,i1+2,cp,n1,1,1);
                this._pushData(i3+9,i1+3,cp,n0,-1,1);
                this._pushData(i3+12,i1+4,cp,n1,-1,1);
                this._pushData(i3+15,i1+5,cp,n1,0,1);
                //
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(i3+18,i1+6,cp,n0,this._arrowWidth,0);
                this._pushData(i3+21,i1+7,cp,n0,-this._arrowWidth,0);
                this._pushData(i3+24,i1+8,np,n1,0,0);
                this._pushData(i3+27,i1+9,cp,n0,-this._arrowWidth,0);
                this._pushData(i3+30,i1+10,np,n1,0,0);
                this._pushData(i3+33,i1+11,np,n1,0,0);
            }else{
                let i3=18+(i-1)*36;
                let i1=6+(i-1)*12;
                n0.subVectors(cp,vs[i-1]).normalize().add(cp);
                n1.copy(np);
                this._pushData(i3,i1,cp,n0,1,1);
                this._pushData(i3+3,i1+1,cp,n0,0,1);
                this._pushData(i3+6,i1+2,cp,n1,1,1);
                this._pushData(i3+9,i1+3,cp,n0,-1,1);
                this._pushData(i3+12,i1+4,cp,n1,-1,1);
                this._pushData(i3+15,i1+5,cp,n1,0,1);
                //
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(i3+18,i1+6,cp,n0,1,1);
                this._pushData(i3+21,i1+7,cp,n0,-1,1);
                this._pushData(i3+24,i1+8,np,n1,1,1);
                this._pushData(i3+27,i1+9,cp,n0,-1,1);
                this._pushData(i3+30,i1+10,np,n1,-1,1);
                this._pushData(i3+33,i1+11,np,n1,1,1);
            }
        }
    }
    _pushLeftArrowData(vs){
        let n0=this._tV0;
        let n1=this._tV1;
        for(let i=0;i<vs.length-1;i++){
            let cp=vs[i];
            let np=vs[i+1];
            if(i===0){
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(0,0,cp,n0,0,0);
                this._pushData(3,1,cp,n0,0,0);
                this._pushData(6,2,np,n1,this._arrowWidth,0);
                this._pushData(9,3,cp,n0,0,0);
                this._pushData(12,4,np,n1,-this._arrowWidth,0);
                this._pushData(15,5,np,n1,this._arrowWidth,0);
            }else{
                let i3=18+(i-1)*36;
                let i1=6+(i-1)*12;
                n0.subVectors(cp,vs[i-1]).normalize().add(cp);
                n1.copy(np);
                this._pushData(i3,i1,cp,n0,1,1);
                this._pushData(i3+3,i1+1,cp,n0,0,1);
                this._pushData(i3+6,i1+2,cp,n1,1,1);
                this._pushData(i3+9,i1+3,cp,n0,-1,1);
                this._pushData(i3+12,i1+4,cp,n1,-1,1);
                this._pushData(i3+15,i1+5,cp,n1,0,1);
                //
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(i3+18,i1+6,cp,n0,1,1);
                this._pushData(i3+21,i1+7,cp,n0,-1,1);
                this._pushData(i3+24,i1+8,np,n1,1,1);
                this._pushData(i3+27,i1+9,cp,n0,-1,1);
                this._pushData(i3+30,i1+10,np,n1,-1,1);
                this._pushData(i3+33,i1+11,np,n1,1,1);
            }
        }
    }
    _pushRightArrowData(vs){
        let n0=this._tV0;
        let n1=this._tV1;
        for(let i=0;i<vs.length-1;i++){
            let cp=vs[i];
            let np=vs[i+1];
            if(i===0){
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(0,0,cp,n0,1,1);
                this._pushData(3,1,cp,n0,-1,1);
                this._pushData(6,2,np,n1,1,1);
                this._pushData(9,3,cp,n0,-1,1);
                this._pushData(12,4,np,n1,-1,1);
                this._pushData(15,5,np,n1,1,1);
            }else if(i===vs.length-2){
                let i3=18+(i-1)*36;
                let i1=6+(i-1)*12;
                n0.subVectors(cp,vs[i-1]).normalize().add(cp);
                n1.copy(np);
                this._pushData(i3,i1,cp,n0,1,1);
                this._pushData(i3+3,i1+1,cp,n0,0,1);
                this._pushData(i3+6,i1+2,cp,n1,1,1);
                this._pushData(i3+9,i1+3,cp,n0,-1,1);
                this._pushData(i3+12,i1+4,cp,n1,-1,1);
                this._pushData(i3+15,i1+5,cp,n1,0,1);
                //
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(i3+18,i1+6,cp,n0,this._arrowWidth,0);
                this._pushData(i3+21,i1+7,cp,n0,-this._arrowWidth,0);
                this._pushData(i3+24,i1+8,np,n1,0,0);
                this._pushData(i3+27,i1+9,cp,n0,-this._arrowWidth,0);
                this._pushData(i3+30,i1+10,np,n1,0,0);
                this._pushData(i3+33,i1+11,np,n1,0,0);
            }else{
                let i3=18+(i-1)*36;
                let i1=6+(i-1)*12;
                n0.subVectors(cp,vs[i-1]).normalize().add(cp);
                n1.copy(np);
                this._pushData(i3,i1,cp,n0,1,1);
                this._pushData(i3+3,i1+1,cp,n0,0,1);
                this._pushData(i3+6,i1+2,cp,n1,1,1);
                this._pushData(i3+9,i1+3,cp,n0,-1,1);
                this._pushData(i3+12,i1+4,cp,n1,-1,1);
                this._pushData(i3+15,i1+5,cp,n1,0,1);
                //
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(i3+18,i1+6,cp,n0,1,1);
                this._pushData(i3+21,i1+7,cp,n0,-1,1);
                this._pushData(i3+24,i1+8,np,n1,1,1);
                this._pushData(i3+27,i1+9,cp,n0,-1,1);
                this._pushData(i3+30,i1+10,np,n1,-1,1);
                this._pushData(i3+33,i1+11,np,n1,1,1);
            }
        }
    }
    _pushData(i3,i1,c,n,o,dm){
        this._offsets[i1]=o;
        this._dashMarks[i1]=dm;
        this._positions[i3]=c.x;
        this._positions[i3+1]=c.y;
        this._positions[i3+2]=c.z;
        this._nextPositions[i3]=n.x;
        this._nextPositions[i3+1]=n.y;
        this._nextPositions[i3+2]=n.z;
    }
    _pushSolidData(vs){
        let n0=this._tV0;
        let n1=this._tV1;
        for(let i=0;i<vs.length-1;i++){
            let cp=vs[i];
            let np=vs[i+1];
            if(i===0){
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(0,0,cp,n0,1,1);
                this._pushData(3,1,cp,n0,-1,1);
                this._pushData(6,2,np,n1,1,1);
                this._pushData(9,3,cp,n0,-1,1);
                this._pushData(12,4,np,n1,-1,1);
                this._pushData(15,5,np,n1,1,1);
            }else{
                let i3=18+(i-1)*36;
                let i1=6+(i-1)*12;
                n0.subVectors(cp,vs[i-1]).normalize().add(cp);
                n1.copy(np);
                this._pushData(i3,i1,cp,n0,1,1);
                this._pushData(i3+3,i1+1,cp,n0,0,1);
                this._pushData(i3+6,i1+2,cp,n1,1,1);
                this._pushData(i3+9,i1+3,cp,n0,-1,1);
                this._pushData(i3+12,i1+4,cp,n1,-1,1);
                this._pushData(i3+15,i1+5,cp,n1,0,1);
                //
                n0.copy(np);
                n1.subVectors(np,cp).normalize().add(np);
                this._pushData(i3+18,i1+6,cp,n0,1,1);
                this._pushData(i3+21,i1+7,cp,n0,-1,1);
                this._pushData(i3+24,i1+8,np,n1,1,1);
                this._pushData(i3+27,i1+9,cp,n0,-1,1);
                this._pushData(i3+30,i1+10,np,n1,-1,1);
                this._pushData(i3+33,i1+11,np,n1,1,1);
            }
        }
    }
}